#include "ai-speech-recognition.h"
#include <functional>
#include <signal.h>

// 静态成员类外初始化
bool AISpeechRecognition::wakeState = false;

AISpeechRecognition::AISpeechRecognition(const string &audioDevice)
    : audioDevice(audioDevice),
      aiThread(nullptr),
      aiThreadRunFlag(false),
      basr(nullptr)
{
    string resourceFile = "resources/common.res";
    float audio_gain = 1;
    bool apply_frontend = false;

    vad = new snowboy::SnowboyVad(resourceFile);
    vad->SetAudioGain(audio_gain);
    vad->ApplyFrontend(apply_frontend);

    // printf("采样率     SampleRate:%d\n", vad->SampleRate());
    // printf("通道       NumChannels:%d\n", vad->NumChannels());
    // printf("采样位数   BitsPerSample:%d\n", vad->BitsPerSample());

    string moduleFileList = "resources/models/meicun.pmdl";
    detector = new snowboy::SnowboyDetect(resourceFile, moduleFileList);
    detector->SetSensitivity("0.6"); // 灵敏度
    detector->SetAudioGain(audio_gain);
    detector->ApplyFrontend(apply_frontend);

    // printf("Num hot words:%d\n", detector->NumHotwords());
    signal(SIGALRM, AISpeechRecognition::handleTimeOut);
}

bool AISpeechRecognition::start(void)
{
    bool ok = alsaSound.open(audioDevice.c_str(), SND_PCM_STREAM_CAPTURE);
    if (!ok)
    {
        fprintf(stderr, "Fail to open alsaSound %s\n", audioDevice.c_str());
        return false;
    }
    pcm_hw_param_t parms = {
        .access = SND_PCM_ACCESS_RW_INTERLEAVED, // 交错模式:SND_PCM_ACCESS_RW_INTERLEAVED
        .format = SND_PCM_FORMAT_S16_LE,         // 无符号16位小端 位深一个样本用多少个二进制标识
        .channels = 1,                           // 通道数
        .sampleRate = 16000,                     // 采样率 每秒采样多少次
        .sampleBits = 16,                        // 16位的二进制标识一个样本
        .frames = 32                             // 一个周期多少祯
    };

    ok = alsaSound.setParams(&parms);
    if (!ok)
    {
        fprintf(stderr, "Fail to alsaSound  setParams \n");
        return false;
    }

    if (!basr)
    {
        printf("获取百度token\n");
        basr = new BaiduAiAsr("jE6Zt83Db5iTHm78PIAi4HUY", "jox6o5Vo5xkx0yypNWHRcdhosNio5IET");
        accessToken = basr->getAccessToken();
        if (accessToken.empty())
        {
            fprintf(stderr, "Fail to baidu getAccessToken ;\n");
            return false;
        }
    }

    if (!aiThread)
    {
        aiThreadRunFlag = true;
        // 开启不停录制音频的功能
        aiThread = new thread(std::bind(&AISpeechRecognition::aiAudioThreadRun, this));
    }

    return true;
}

void AISpeechRecognition::stop(void)
{
    aiThreadRunFlag = false;
}

void AISpeechRecognition::aiAudioThreadRun(void)
{
    uint32_t time = 200; // 300毫秒
    vector<uint8_t> audioData;
    while (aiThreadRunFlag)
    {
        // 录制 0.3秒
        bool ok = alsaSound.record(time, audioData);
        if (!ok)
        {
            break;
        }
        // 静音检测
        int ret = vad->RunVad((int16_t *)audioData.data(), audioData.size() / 2);
        // printf("run vad ret:%d \n", ret);
        //  静音状态
        if (ret < 0)
        {
            if (!userSpeechData.empty())
            {
                printf("用户静音状态 去做识别了\n");
                // 暂停录音 仿照管破裂
                alsaSound.pause();
                // 语音识别
                recognitionUserSpeedchData();
                // 恢复录音
                alsaSound.resume();
            }
            continue;
        }

        // 非静音状态
        printf("用户非静音状态\n");
        // printf("Detect No-silence\n");
        // 如果用户在说话，则记录用户说话的数据，说完话则识别
        userSpeechData.insert(userSpeechData.end(), audioData.begin(), audioData.end());
    }
}

void AISpeechRecognition::recognitionUserSpeedchData(void)
{
    if (wakeState)
    {
        // 识别用户的声音
        printf("识别用户的声音\n");
        string audioText = basr->asrAudioData(accessToken, userSpeechData);
        cout << "AudioText : " << audioText << endl;
    }
    else
    {
        int ret = detector->RunDetection((int16_t *)userSpeechData.data(), userSpeechData.size() / 2);
        printf("识别用户的结果 :%d\n", ret);
        if (ret > 0)
        { // 设备已经唤醒了
            playAckAudio();
            sleep(1); // 消除回音加延时
            wakeState = true;
            // 唤醒后10秒有效
            alarm(10);
        }
        printf("run RunDetection ret:%d\n", ret);
    }
    userSpeechData.clear();
}

/**
 * 播放回复语音
 */
void AISpeechRecognition::playAckAudio(void)
{
    srand(time(nullptr));
    int num = rand() % 4 + 1;
    char palyCMD[1024];
    sprintf(palyCMD, "aplay resources/wav/%d.wav", num);
    system(palyCMD);
}

void AISpeechRecognition::handleTimeOut(int signum)
{
    wakeState = false;
}
